/*


 ************************************************************************************
 v1.3-2024-1-30 
  去掉舵机，增加usb打印
  alarm改为
  RTC clock source: 从LSI改为LSE（外部32K晶体）
  控制器改为stm32f103c8
  问题: 休眠唤醒后串口无法使用，
    IR无法唤醒，程序设置为down键才能唤醒。其他键唤醒后进入idle状态，但time没有初始化为0；

v1.4-2024_02_20
  1.增加智能配网功能,采用smartconfg 一键配置方案. *键启动和停止
  2.key定义修改,时间和浇水组合.
  3.增加初始化init模式
  4.优化网络模块

v1.5-2024_02_20
  1、修改上报数据格式：运行时间 0.01小时。
  2、增加准备休眠模式，运行2秒。
  3、单板修改；红外增加10uf电容，水泵改为pa11
  4、修改遥控器时间模式定义，数字0为测试，数字1~5为间隔天数。浇水时间可配置通过g_SleepTimeCoff设置大花瓶60、小花瓶5。
  5、绿色指示灯改为可闪烁，实际模式时n短1长，n代表对应的数字。wifi配置时连续闪烁。其他模式常亮。红外信号时快速闪烁。
以上测试ok
  6、修改上报数据，工作状态改为水泵状态，电压改为唤醒原因 1硬件，2定时,3复位，
。
v1.6-2024_02_25
  1、外部中断唤醒后休眠时间增长问题。
  -方案1：芯片C6TX小内存不能使用getesop，沿用deepsleep仅在时间设置或定时任务触发后才进行休眠及时。
  2、手动唤醒不增加配网络

。
v1.6A1.1-2024_03_04
  1、修复bug：手动模式没有进入休眠
  2、修复bug：操作了时间设置后，定时任务从每天7点改为了9点。需求不明确。
  3、更新STM32duino Low Power@1.2.5，解决日期bug

v1.7-2024_03_15_ST_001
  1、删除，数据超范围被丢失：上报运行时间包括休眠时间+运行时间,时间单位改为秒。--不落入
  2、修改ir唤醒电路，唤醒pin改为A1高电平唤醒---不落入
  3、时间设置导致无法退出BUG。因为config->g_SSTimer = 0清除状态机时间，需删除且仅在执行按键时清除。
 */



/*
 * Specify which protocol(s) should be used for decoding.
 * If no protocol is defined, all protocols (except Bang&Olufsen) are active.
 * This must be done before the #include <IRremote.hpp>
 */



// #include <ArduinoJson.h>

#include <birdwater.h>
#include <MqttReq.h>

#include <Arduino.h>

#include <STM32RTC.h>
#include <rtc.h>
#include <STM32LowPower.h>
#include <low_power.h>
// #include <Servo.h>


// /* Get the rtc object */
STM32RTC& rtc = STM32RTC::getInstance();

/* Change this value to set alarm match offset in millisecond */
/* Note that STM32F1xx does not manage subsecond only second */
static uint32_t atime = 567;

// Declare it volatile since it's incremented inside an interrupt
volatile int alarmMatch_counter = 0;

// Variables for RTC configurations
static byte seconds = 0;
static byte minutes = 0;
static byte hours = 0;

static byte weekDay = 1;
static byte day = 1;
static byte month = 1;
static byte year = 18;



// Servo fs90r;  // create servo object to control a servo

int potpin = 0;    // analog pin used to connect the potentiometer
int val;           // variable to read the value from the analog pin
int servopin = 9;  // pin used to connect the servo

#define LED_BUILTIN PC13    // 指示灯
#define SEVRO_OUT PA12      // 舵机
#define WATERBUMP_OUT PA11  //PA9  // 水泵
#define KEY1 PA1            // KEY
#define KEY2 PA2            // KEY
#define KEY3 PA0            // KEY
#define POWERON PB12        //电源

// PA9 串口






int keyscan(void) {
  int key;
  //---key
  if (!digitalRead(KEY1)) {
    key = 1;
  } else if (!digitalRead(KEY2)) {
    key = 2;
  } else if (!digitalRead(KEY3)) {
    key = 3;
  } else {
    key = 0;
  }

  return key;
}

HardwareSerial Serial1(PA10, PA9);  //注意，在STM32中，一般大引脚序号在前，小序号在后



void setup() {
  
  // put your setup code here, to run once:
  Serial1.begin(115200);
  Serial.begin(115200);
  // pinMode(9,OUTPUT);

  rtc.begin();
  rtc.setTime(hours, minutes, seconds);
  rtc.setDate(weekDay, day, month, year);

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(PB12, OUTPUT);
  pinMode(WATERBUMP_OUT, OUTPUT);

  pinMode(KEY1, INPUT_PULLDOWN);
  pinMode(KEY2, INPUT);
  pinMode(KEY3, INPUT_PULLUP);  //唤醒


  // fs90r.attach(SEVRO_OUT);  // attaches the servo on pin 9 to the servo object

  // Configure low power
  LowPower.begin();
  LowPower.enableWakeupFrom(&rtc, alarmMatch, &atime);
  LowPower.enableWakeupFrom(&Serial1, SerialWakeup);

  //HIGH, LOW, RISING, FALLING or CHANGE
  //IDLE_MODE, SLEEP_MODE, DEEP_SLEEP_MODE or SHUTDOWN_MODE
  // LowPower.attachInterruptWakeup(KEY1, repetitionsIncrease, HIGH, DEEP_SLEEP_MODE);
  // LowPower.attachInterruptWakeup(KEY3, repetitionsIncrease, LOW, DEEP_SLEEP_MODE);
  
  LowPower.deepSleep(3600 * 1000);  //休眠1小时,重写不进stop  

  digitalWrite(PB12, HIGH);

  BirdWaterInit(&g_BirdWaterConfig);
  // BirdWaterTaskEn(&g_BirdWaterConfig);
  g_BirdWaterConfig.g_SleepTime = 24*3600;      //24小时
  g_BirdWaterConfig.g_WaterTime = 60;   //工作时间 3*60秒
  g_BirdWaterConfig.wakeupsourse = 3;
  g_BirdWaterConfig.workTimePre = millis();

  // g_BirdWaterConfig.g_WorkState = WORK_STATE_STUDY; //重启默认配网
  g_BirdWaterConfig.g_Key = keyscan() ;
  
  if (g_BirdWaterConfig.g_Key == 1)
  {
    MQTTStartConfgFun();
  }

  if(! MQTTConnectFun())  //网络配置
  {
    //网络连接失败
    Serial.println("网络连接失败");
  }



  MQTTInit();
}

#define TASK1_PERIOD 100
int t1 = TASK1_PERIOD;  //设led1每200ms改变状态

/*

*/
void Loop100ms(void) {

  if (millis() > t1) {
    t1 = millis() + TASK1_PERIOD;
    //
    g_BirdWaterConfig.g_SSTimer++;
    LEDShow();
    // birdwaterTimer100ms();
  }
}
//
int g_IrReceiverCmd = 0;
int WorkStatePre = 0;

void loop() {
  // put your main code here, to run repeatedly:
  int state = 0;
  int i = 0;

  g_BirdWaterConfig.g_Key = keyscan();

  MQTTDispack();
  MQTTUpdate();
  
  //---
  Loop100ms();
  BirdWaterLoop(&g_BirdWaterConfig);

  // val = map(g_BirdWaterConfig.g_MoveSpeed, -100, 100, 0, 180);  // scale it to use it with the servo (value between 0 and 180)
  // fs90r.write(val);
  // digitalWrite(WATERBUMP_OUT, g_BirdWaterConfig.g_PumpOn);
  digitalWrite(WATERBUMP_OUT, g_BirdWaterConfig.g_PumpOn);

  if (g_BirdWaterConfig.g_WorkState == WORK_STATE_TEST)
  {
    LowPower.deepSleep(3600 * 1000);  //休眠1小时,重写不进stop

    alarmMatch_counter = 0;
  }
  
  if ((g_BirdWaterConfig.g_WorkState != WORK_STATE_WORK))  //无按键情况下休眠
  {
    digitalWrite(PB12, LOW);          //关电源

   
  }
  else
  {
    digitalWrite(PB12, HIGH);  //打开电源
  }

  // //非工作状态下复位一次网络
  // if ((g_BirdWaterConfig.g_WorkState != WORK_STATE_WORK)  && (!g_BirdWaterConfig.g_PumpOn) && (ReceiveState.ErrNum > 10))
  // {
  //   MQTTConnectFun();  //网络配置
  //   MQTTInit();
  // }

  if ((g_BirdWaterConfig.g_WorkState == WORK_STATE_SLEEP) || (g_BirdWaterConfig.g_WorkState == WORK_STATE_PREPARE_SLEEP))  //无按键情况下休眠
  {
      //digitalWrite(LED_BUILTIN, HIGH);    //关闭指示灯
      g_BirdWaterConfig.wakeupsourse = 0; 
  }

  if ((g_BirdWaterConfig.g_WorkState == WORK_STATE_SLEEP))  //无按键情况下休眠
  {
    // Serial1.end();                    //关闭通信
    // LowPower.deepSleep(3*1000);        //休眠3秒      
    if (g_BirdWaterConfig.g_SleepTime < 3600)
    {
      LowPower.deepSleep(g_BirdWaterConfig.g_SleepTime * 1000);  //按照时间时间休眠    
    }
    //
    
    LowPower.deepSleep(3*1000);
    LowPower.stop(); //进入stop
    
    Serial1.begin(115200);
    pinMode(WATERBUMP_OUT, OUTPUT);
    delay(100);
    BirdWaterInit(&g_BirdWaterConfig);

    //    
  }
}

//定时器唤醒
void alarmMatch(void* data) {
  LowPower.deepSleep(3600 * 1000);  //再次休眠1小时

  alarmMatch_counter++;
  g_BirdWaterConfig.wakeupsourse = 2;

  if (alarmMatch_counter >= g_BirdWaterConfig.g_SleepTime/3600) {
    alarmMatch_counter = 0;
    BirdWaterTaskEn(&g_BirdWaterConfig);  //打开自动执行任务
    // BirdWaterInit(&g_BirdWaterConfig);
  }
}

int ledflashcount = 0;
int ledtime = 0;
int leddelay = 0;
int ledstate = 0;
int ledstep = 0;

//100ms任务 开启显示 0：全亮，100长闪
void LEDShow(void) {

  if (g_BirdWaterConfig.g_WorkState == WORK_STATE_STUDY) 
  {
     ledflashcount = 100;
  }
  else if (g_BirdWaterConfig.g_WorkState == WORK_STATE_WORK)
  {
    ledflashcount = 0;
  }
  else if (g_BirdWaterConfig.g_WorkState == WORK_STATE_TIMESET)
  {
    ledflashcount = g_BirdWaterConfig.g_TimeConfig+1;
  }
  else if (g_BirdWaterConfig.g_WorkState == WORK_STATE_PREPARE_SLEEP) 
  {
     ledflashcount = 1;
  }else
  {
    ledflashcount = 0;
  }

  
  if (ledtime++ >= leddelay) {
    ledtime = 0;

    if (ledstep++ >= ledflashcount * 2) {
      ledstep = 0;
      leddelay = 5;
      ledstate = 1;
    } else {
      leddelay = 1;
      ledstate = !ledstate;
    }
  }


  if (ReceiveState.ready == 0)  //无按键情况下休眠
  {
    digitalWrite(LED_BUILTIN, HIGH);  //关指示灯
  } else {
    digitalWrite(LED_BUILTIN, LOW);  //打开指示灯
  }
}


//硬件唤醒
void repetitionsIncrease() {
  // This function will be called once on device wakeup
  // You can do some little operations here (like changing variables which will be used in the loop)
  // Remember to avoid calling delay() and long running functions since this functions executes in interrupt context
  //digitalWrite(LED_BUILTIN, LOW);    //打开指示灯
  //BirdWaterInit(&g_BirdWaterConfig);
  //BirdWaterTaskEn(&g_BirdWaterConfig);  //打开自动执行任务
  g_BirdWaterConfig.wakeupsourse = 1;  
  
  //digitalWrite(LED_BUILTIN, HIGH);  //关闭指示灯
}

void SerialWakeup() {
  // This function will be called once on device wakeup
  // You can do some little operations here (like changing variables
  // which will be used in the loop)
  // Remember to avoid calling delay() and long running functions
  // since this functions executes in interrupt context
  g_BirdWaterConfig.wakeupsourse = 1;  

}


